package com.kyin.satoken.utils;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.kyin.satoken.exception.GlobalException;

import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

/**
 * 加密解密工具类（适配Hutool 5.8.38版本）
 * 对应前端加密逻辑：AES-CBC加密数据 + RSA加密AES密钥 + HMAC-SHA256签名
 */
public class CryptoUtil {

    // 时间戳有效窗口：5分钟（毫秒）
    private static final long TIMESTAMP_WINDOW = 300000;
    private static final String PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMyYOwtPf/tgzSaXkO0NkgfQ9QP+x2ar7KlAkHa75QnuX/Q47Eq8luZWESltvi6HWVyEAeisxTa/Ydu+cNi+fvRAo/fvSR145k5GGBibGn+TXu/4I2gDEje/2Meu8COgqQq6OC3BL27fCmYkbGynOwe5F74Qn+lmfmsJrJ2K48bFAgMBAAECgYAyJurqVd2YgF0ppaO4CEiQs+8rwIuepmxH/PSstlhpu843rrB1jY8r/VMNpcIl47XLEOzq3VcmmXlihWdiKigyRTJOZJfIdEW6KOavH172XIhRDgwd2va3/kILxRnLremwjX93gKTTpxuJYcAY3QhVnixiOuYX1Wx+dkWegi0PzQJBAPMYx4Ubn0+MbfOsHwkKVTwNMmsqy+eXKG7M3FhbcdfneM2x9yBZBMAWJAhSA6cRUoEWQFJFnNi4lY+ZXL031+sCQQDXdEffgZkv1XaY8BPtNiS1Q0cdLro9WZSdjRcP7kk4vkepoLKifVTpgRYXjdHJ8rBh3iPtjD6SAZ/0O8/XcmAPAkBUpXgz3R5n8JoIkxJrb3Gma2H29vvxNC0cB7FiAtVtPGhQF3vZg6WnzQ3W59flwJ5d3IWhICQxQnSxf9FPAbbpAkEAjRmyGEYftJvv16ki468ZyzwMTqhTf+oy0huchupfObIj7Is5cgDyoxnPBg3y3nb53J5GmUPHlodJ4gFp1IcrMQJBAKlTLaFLJi7Pu/J62C4CXBUEdlSZ///bifPt7rutMBalsvVzxg5tZsssFprMBn/tuH8m4WtMHDsQOfKdg1fysRM=";
    private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMmDsLT3/7YM0ml5DtDZIH0PUD/sdmq+ypQJB2u+UJ7l/0OOxKvJbmVhEpbb4uh1lchAHorMU2v2HbvnDYvn70QKP370kdeOZORhgYmxp/k17v+CNoAxI3v9jHrvAjoKkKujgtwS9u3wpmJGxspzsHuRe+EJ/pZn5rCaydiuPGxQIDAQAB";
    private static final String HMAC_KEY = "5687d47e8e816480261d8a83eeacb3619cba7e38a3c41fc7a6bed45051f2ec47";

    private static final String PARAM_ENCRYPTED_DATA_KEY = "encryptedData";
    private static final String PARAM_ENCRYPTED_KEY = "encryptedKey";
    private static final String PARAM_IV_KEY = "iv";
    private static final String PARAM_TIMESTAMP_KEY = "timestamp";
    private static final String PARAM_NONCE_KEY = "nonce";
    private static final String PARAM_SIGNATURE_KEY = "signature";
    private static final String PARAM_CIPHERTEXT_KEY = "ciphertext";



    /**
     * 生成RSA公私钥对（2048位）
     * Hutool 5.8.38默认使用2048位密钥，符合安全标准
     * @return 包含公私钥的Map，publicKey为公钥，privateKey为私钥（均为Base64编码）
     */
    private static Map<String, String> generateRsaKeyPair() {
        KeyPair keyPair = SecureUtil.generateKeyPair(AsymmetricAlgorithm.RSA.getValue());
        String publicKey = Base64.encode(keyPair.getPublic().getEncoded());
        String privateKey = Base64.encode(keyPair.getPrivate().getEncoded());

        Map<String, String> keyMap = new HashMap<>(2);
        keyMap.put("publicKey", publicKey);
        keyMap.put("privateKey", privateKey);
        return keyMap;
    }

    /**
     * 使用RSA私钥解密AES密钥
     * Hutool 5.8.38对RSA解密进行了稳定性优化
     * @param encryptedAesKey 加密后的AES密钥（Base64编码）
     * @return 解密后的AES密钥
     */
    private static String decryptAesKeyWithRsa(String encryptedAesKey) {
        RSA rsa = new RSA(PRIVATE_KEY, null);
        // Hutool 5.8.38中KeyType枚举值已统一为大写，PRIVATE为正确值
        return rsa.decryptStr(encryptedAesKey, KeyType.PrivateKey, StandardCharsets.UTF_8);
    }

    /**
     * 使用AES-CBC模式解密数据
     * Hutool 5.8.38修复了AES解密中部分边缘情况的处理
     * @param encryptedData 加密的数据（Base64编码）
     * @param aesKey AES密钥（Base64编码）
     * @param iv 初始化向量（Base64编码）
     * @return 解密后的明文
     */
    private static String decryptDataWithAes(String encryptedData, String aesKey, String iv) {
        // Hutool 5.8.38推荐使用of()工厂方法创建AES实例，确保算法参数正确
        AES aes = new AES(
                Mode.CBC, Padding.PKCS5Padding,
                Base64.decode(aesKey),
                Base64.decode(iv)
        );
        return aes.decryptStr(encryptedData, StandardCharsets.UTF_8);
    }

    /**
     * 验证HMAC签名
     * Hutool 5.8.38优化了HMac的性能和稳定性
     * @param timestamp 时间戳
     * @param nonce 随机数
     * @param encryptedData 加密的数据
     * @param signature 待验证的签名（十六进制）
     * @return 验证是否通过
     */
    private static boolean verifySignature(String timestamp, String nonce, String encryptedData,
                                          String signature) {
        // 验证时间戳有效性
        long currentTime = System.currentTimeMillis();
        long requestTime;
        try {
            requestTime = Long.parseLong(timestamp);
        } catch (NumberFormatException e) {
            return false;
        }
        // 检查时间戳是否在有效窗口内，防止重放攻击
        if (Math.abs(currentTime - requestTime) > TIMESTAMP_WINDOW) {
            return false;
        }

        // 生成待签名字符串
        String signStr = timestamp + nonce + encryptedData;

        // Hutool 5.8.38中HMac.of()方法处理密钥更稳定
        HMac hmac =new HMac(HmacAlgorithm.HmacSHA256, HMAC_KEY.getBytes(StandardCharsets.UTF_8));
        String calculatedSignature = hmac.digestHex(signStr);

        // 比较签名（忽略大小写，兼容前端可能的大小写输出）
        return calculatedSignature.equalsIgnoreCase(signature);
    }

    /**
     * 完整的解密流程
     * @return 解密后的业务数据
     */
    public static String decryptSecurePayload(Map<String , Object> params)  {
        String encryptedData = MapUtil.getStr(params, PARAM_ENCRYPTED_DATA_KEY); //
        String encryptedKey = MapUtil.getStr(params, PARAM_ENCRYPTED_KEY); // 加密的AES密钥
        String iv = MapUtil.getStr(params, PARAM_IV_KEY); // 初始化向量
        String timestamp = MapUtil.getStr(params, PARAM_TIMESTAMP_KEY); // 时间戳
        String nonce = MapUtil.getStr(params, PARAM_NONCE_KEY); // 随机数
        String signature = MapUtil.getStr(params, PARAM_SIGNATURE_KEY); // 签名

        // 1. 验证签名
        if (!verifySignature(timestamp, nonce, encryptedData, signature)) {
            throw new GlobalException("签名验证失败");
        }

        // 2. 解密AES密钥
        String aesKey = decryptAesKeyWithRsa(encryptedKey);

        // 3. 解密数据
        return decryptDataWithAes(encryptedData, aesKey, iv);
    }


    /**
     * 生成随机AES密钥（16字节，128位）
     * @return Base64编码的AES密钥
     */
    private static String generateAesKey() {
        byte[] key = new byte[16];
        new SecureRandom().nextBytes(key);
        return Base64.encode(key);
    }


    /**
     * 使用AES-CBC模式加密数据
     * @param data 明文数据
     * @param aesKey Base64编码的AES密钥
     * @return 包含加密数据和IV的Map
     */
    private static Map<String, String> encryptDataWithAes(String data, String aesKey) {
        // 生成随机IV（16字节）
        byte[] iv = new byte[16];
        new SecureRandom().nextBytes(iv);

        // 解密AES密钥（Base64解码）
        byte[] keyBytes = Base64.decode(aesKey);

        // 初始化AES加密器
        AES aes = new AES(Mode.CBC, Padding.PKCS5Padding, keyBytes, iv);

        // 执行加密
        String encryptedData = aes.encryptBase64(data, StandardCharsets.UTF_8);

        // 返回加密结果和IV
        Map<String, String> result = new HashMap<>(2);
        result.put(PARAM_CIPHERTEXT_KEY, encryptedData);
        result.put(PARAM_IV_KEY, Base64.encode(iv));
        return result;
    }

    /**
     * 使用RSA公钥加密AES密钥
     * @param aesKey AES密钥
     * @return 加密后的AES密钥（Base64编码）
     */
    private static String encryptAesKeyWithRsa(String aesKey) {
        RSA rsa = new RSA(null, PUBLIC_KEY);
        return rsa.encryptBase64(aesKey, StandardCharsets.UTF_8, KeyType.PublicKey);
    }


    /**
     * 生成HMAC-SHA256签名
     * @param timestamp 时间戳
     * @param nonce 随机数
     * @param encryptedData 加密的数据
     * @return 十六进制签名
     */
    private static String generateSignature(String timestamp, String nonce, String encryptedData) {
        String signStr = timestamp + nonce + encryptedData;
        HMac hmac = new HMac(HmacAlgorithm.HmacSHA256, HMAC_KEY.getBytes(StandardCharsets.UTF_8));
        return hmac.digestHex(signStr);
    }

    /**
     * 构建安全请求载荷（加密方法）
     * @param data 要加密的业务数据（对象或字符串）
     * @return 加密后的请求载荷
     */
    public static Map<String, Object> buildSecurePayload(Object data) {
        // 1. 准备基础组件
        String timestamp = String.valueOf(System.currentTimeMillis());
        String nonce = IdUtil.fastSimpleUUID(); // 生成随机数
        String dataStr = data instanceof String ? (String) data : JSONUtil.toJsonStr(data);

        // 2. 生成AES密钥并加密业务数据
        String aesKey = generateAesKey();
        Map<String, String> aesEncryptedResult = encryptDataWithAes(dataStr, aesKey);

        // 3. 使用RSA公钥加密AES密钥
        String encryptedAesKey = encryptAesKeyWithRsa(aesKey);

        String ciphertext = MapUtil.getStr(aesEncryptedResult, PARAM_CIPHERTEXT_KEY);
        // 4. 生成HMAC签名
        String signature = generateSignature(timestamp, nonce,  ciphertext);

        // 5. 组装最终请求载荷
        Map<String, Object> payload = new HashMap<>(6);
        payload.put(PARAM_ENCRYPTED_DATA_KEY, ciphertext);
        payload.put(PARAM_ENCRYPTED_KEY, encryptedAesKey);
        payload.put(PARAM_IV_KEY, aesEncryptedResult.get("iv"));
        payload.put(PARAM_TIMESTAMP_KEY, timestamp);
        payload.put(PARAM_NONCE_KEY, nonce);
        payload.put(PARAM_SIGNATURE_KEY, signature);

        return payload;
    }


    public static void main(String[] args) throws Exception {
        JSONObject jsonObject = new JSONObject();
        jsonObject.set("id", "123");
        jsonObject.set("name", "张三");
        // 加密数据
        Map<String, Object> objectMap = buildSecurePayload(jsonObject);
        // 解密数据
        String payload = decryptSecurePayload(objectMap);
        System.out.println("payload = " + payload);
    }
}
